package sys

import (
	"fmt"
	"runtime"
	"time"
)

var startTime = time.Now()

type Status struct {
	Uptime       string `json:"uptime,omitempty"`
	NumGoroutine int    `json:"num_goroutine,omitempty"`

	// General statistics.
	MemAllocated string `json:"mem_allocated"` // bytes allocated and still in use
	MemTotal     string `json:"mem_total"`     // bytes allocated (even if freed)
	MemSys       string `json:"mem_sys"`       // bytes obtained from system (sum of XxxSys below)
	Lookups      uint64 `json:"lookups"`       // number of pointer lookups
	MemMallocs   uint64 `json:"mem_mallocs"`   // number of malloc
	MemFrees     uint64 `json:"mem_frees"`     // number of frees

	// Main allocation heap statistics.
	HeapAlloc    string `json:"heap_alloc"`    // bytes allocated and still in use
	HeapSys      string `json:"heap_sys"`      // bytes obtained from system
	HeapIdle     string `json:"heap_idle"`     // bytes in idle spans
	HeapInuse    string `json:"heap_inuse"`    // bytes in non-idle span
	HeapReleased string `json:"heap_released"` // bytes released to the OS
	HeapObjects  uint64 `json:"heap_objects"`  // total number of allocated objects

	// Low-level fixed-size structure allocator statistics.
	//	Inuse is bytes used now.
	//	Sys is bytes obtained from system.
	StackInuse  string `json:"stack_inuse"` // bootstrap stacks
	StackSys    string `json:"stack_sys"`
	MSpanInuse  string `json:"m_span_inuse"`
	MSpanSys    string `json:"m_span_sys"`
	MCacheInuse string `json:"m_cache_inuse"`
	MCacheSys   string `json:"m_cache_sys"`
	BuckHashSys string `json:"buck_hash_sys"` // profiling bucket hash table
	GCSys       string `json:"gc_sys"`        // GC metadata
	OtherSys    string `json:"other_sys"`     // other system allocations

	// Garbage collector statistics.
	NextGC       string `json:"next_gc"` // next run in HeapAlloc time (bytes)
	LastGC       string `json:"last_gc"` // last run in absolute time (ns)
	PauseTotalNs string `json:"pause_total_ns"`
	PauseNs      string `json:"pause_ns"` // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256]
	NumGC        uint32 `json:"num_gc"`
}

func GetStatus() (status Status) {
	m := new(runtime.MemStats)
	runtime.ReadMemStats(m)

	status.Uptime = timeSince(startTime)
	status.NumGoroutine = runtime.NumGoroutine()

	status.MemAllocated = unitBytes(m.Alloc)
	status.MemTotal = unitBytes(m.TotalAlloc)
	status.MemSys = unitBytes(m.Sys)
	status.Lookups = m.Lookups
	status.MemMallocs = m.Mallocs
	status.MemFrees = m.Frees

	status.HeapAlloc = unitBytes(m.HeapAlloc)
	status.HeapSys = unitBytes(m.HeapSys)
	status.HeapIdle = unitBytes(m.HeapIdle)
	status.HeapInuse = unitBytes(m.HeapInuse)
	status.HeapReleased = unitBytes(m.HeapReleased)
	status.HeapObjects = m.HeapObjects

	status.StackInuse = unitBytes(m.StackInuse)
	status.StackSys = unitBytes(m.StackSys)
	status.MSpanInuse = unitBytes(m.MSpanInuse)
	status.MSpanSys = unitBytes(m.MSpanSys)
	status.MCacheInuse = unitBytes(m.MCacheInuse)
	status.MCacheSys = unitBytes(m.MCacheSys)
	status.BuckHashSys = unitBytes(m.BuckHashSys)
	status.GCSys = unitBytes(m.GCSys)
	status.OtherSys = unitBytes(m.OtherSys)

	status.NextGC = unitBytes(m.NextGC)
	status.LastGC = fmt.Sprintf("%.1fs", float64(time.Now().UnixNano()-int64(m.LastGC))/1000/1000/1000)
	status.PauseTotalNs = fmt.Sprintf("%.1fs", float64(m.PauseTotalNs)/1000/1000/1000)
	status.PauseNs = fmt.Sprintf("%.3fs", float64(m.PauseNs[(m.NumGC+255)%256])/1000/1000/1000)
	status.NumGC = m.NumGC

	return status
}
